package com.xxl.sso.core.util;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 带过期策略的缓存
 *
 * @author chenlongfei
 * @date 2021-02-18
 */
public class CacheManager {
    private static Map<String, CacheData> CACHE_DATA = new ConcurrentHashMap<>();

    /**
     * 每隔30秒清除过期的key
     */
    static {
        Runnable runnable = new Runnable() {
            public void run() {
                for (Map.Entry<String, CacheData> entry : CACHE_DATA.entrySet()) {
                    String key = entry.getKey();
                    CacheData data = CACHE_DATA.get(key);
                    if (data != null && (data.getExpire() > 0 && data.getSaveTime() <= System.currentTimeMillis())) {
                        CACHE_DATA.remove(key);
                    }
                }
            }
        };
        ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
        service.scheduleAtFixedRate(runnable, 0, 30, TimeUnit.SECONDS);
    }

    public static <T> T getData(String key, Load<T> load, int expire) {
        T data = getData(key);
        if (data == null && load != null) {
            data = load.load();
            if (data != null) {
                setData(key, data, expire);
            }
        }
        return data;
    }

    public static <T> T getData(String key) {
        if (key == null) {
            return null;
        }
        CacheData<T> data = CACHE_DATA.get(key);
        if (data != null && (data.getExpire() <= 0 || data.getSaveTime() >= System.currentTimeMillis())) {
            return data.getData();
        } else if (data != null) {
            /**
             * 如果存活时间到期，删除key
             */
            CACHE_DATA.remove(key);
        }
        return null;
    }

    public static <T> void setData(String key, T data, int expire) {
        CACHE_DATA.put(key, new CacheData(data, expire));
    }

    public static <T> void setData(String key, T data) {
        CACHE_DATA.put(key, new CacheData(data, -1));
    }

    public static <T> void expireData(String key, int expire) {
        T data = getData(key);
        if (data != null) {
            setData(key, data, expire);
        }
    }

    public static void clear(String key) {
        if (key != null) {
            CACHE_DATA.remove(key);
        }
    }

    public static void clearAll() {
        CACHE_DATA.clear();
    }

    public interface Load<T> {
        T load();
    }

    private static class CacheData<T> {
        CacheData(T t, int expire) {
            this.data = t;
            this.expire = expire <= 0 ? 0 : expire * 1000;
            this.saveTime = System.currentTimeMillis() + this.expire;
        }

        private T data;
        private long saveTime;
        private long expire;

        public T getData() {
            return data;
        }

        public long getExpire() {
            return expire;
        }

        public long getSaveTime() {
            return saveTime;
        }
    }

}
